/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.songlin.houxin.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.plugin.excel.vo.ErrorMessage;
import com.songlin.houxin.admin.api.dto.*;
import com.songlin.houxin.admin.api.entity.Channel;
import com.songlin.houxin.admin.api.entity.Order;
import com.songlin.houxin.admin.api.enums.LevelEnum;
import com.songlin.houxin.admin.api.vo.ActiveStatisticVO;
import com.songlin.houxin.admin.api.vo.BasePageVO;
import com.songlin.houxin.admin.api.vo.OrderVO;
import com.songlin.houxin.admin.api.vo.StatisticVO;
import com.songlin.houxin.admin.listeners.StatisticsEvent;
import com.songlin.houxin.admin.mapper.OrderMapper;
import com.songlin.houxin.admin.service.ChannelService;
import com.songlin.houxin.admin.service.OrderService;
import com.songlin.houxin.common.core.exception.BusinessAssert;
import com.songlin.houxin.common.core.util.R;
import com.songlin.pig.common.security.service.PigUser;
import com.songlin.pig.common.security.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 订单表
 *
 * @author pig code generator
 * @date 2022-03-06 08:13:05
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    public static final String YYYY = "yyyy";
    public static final String YYYY_MM = "yyyy-MM";
    public static final String YYYY_MM_DD = "yyyy-MM-dd";
    public static final int SECONDS = 86399;
    private final ChannelService channelService;
    private final OrderMapper orderMapper;
    private final ApplicationEventPublisher applicationEventPublisher;
    private final RedisTemplate<String, Object> redisTemplate;
    private static final String STATISTIC_INFO = "statistic_info";

    @Override
    public StatisticVO queryStatisticInfo(ActiveStatisticDTO dto) {
        StatisticActiveCount statisticActiveCount = StatisticActiveCount.builder()
                .channelId(dto.getChannelId())
                .build();
        Optional.ofNullable(dto.getYearTime()).ifPresent(year -> {
            DateTime yearDateTime = DateUtil.parse(year, YYYY);
            LocalDateTime yearDate = LocalDateTime.ofInstant(yearDateTime.toInstant(), ZoneId.systemDefault());
            statisticActiveCount.setStart(yearDate);
            statisticActiveCount.setDate(year);
            LocalDateTime end = yearDate.with(TemporalAdjusters.lastDayOfYear());
            statisticActiveCount.setEnd(end.plusSeconds(SECONDS));
        });
        Optional.ofNullable(dto.getMonthTime()).ifPresent(month -> {
            DateTime monthTime = DateUtil.parse(month, YYYY_MM);
            LocalDateTime monthDate = LocalDateTime.ofInstant(monthTime.toInstant(), ZoneId.systemDefault());
            LocalDateTime monthStartDay = LocalDate.of(monthDate.getYear(), monthDate.getMonth(), 1).atStartOfDay();
            statisticActiveCount.setStart(monthStartDay);
            statisticActiveCount.setDate(month);
            LocalDateTime end = monthStartDay.with(TemporalAdjusters.lastDayOfMonth());
            statisticActiveCount.setEnd(end.plusSeconds(SECONDS));
        });
        Optional.ofNullable(dto.getDateTime()).ifPresent(dateTime -> {
            DateTime dates = DateUtil.parse(dateTime, YYYY_MM_DD);
            LocalDateTime date = LocalDateTime.ofInstant(dates.toInstant(), ZoneId.systemDefault());
            statisticActiveCount.setDate(dateTime);
            statisticActiveCount.setStart(LocalDateTime.of(date.toLocalDate(), LocalTime.MIN));
            statisticActiveCount.setEnd(LocalDateTime.of(date.toLocalDate(), LocalTime.MAX));
        });
        StatisticVO statisticVO = statisticActiveCount(statisticActiveCount);
        return statisticVO;

    }

    @Override
    public ActiveStatisticVO queryActiveStatisticInfo(ActiveStatisticDTO dto) {
        // 当天的开始时间
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime currentDayStart = LocalDateTime.of(now.toLocalDate(), LocalTime.MIN);//当天零点
        // 统计当日的激活信息
        StatisticVO dateStatisticVO = statisticActiveCount(currentDayStart);
        dateStatisticVO.setDate(DateUtil.format(now, YYYY_MM_DD));
        // 统计当月的激活信息
        // 当月的开始时间
        LocalDateTime monthStartDay = LocalDate.of(now.getYear(), now.getMonth(), 1).atStartOfDay();
        StatisticVO monthStatisticVO = statisticActiveCount(monthStartDay);
        monthStatisticVO.setDate(DateUtil.format(now, YYYY_MM));
        LocalDateTime start = now.with(TemporalAdjusters.firstDayOfYear());
        // 年统计信息
        StatisticVO yearStatisticVO = statisticActiveCount(start, now);
        yearStatisticVO.setDate(DateUtil.format(now, YYYY));
        // 总激活量
        StatisticVO totalStatisticVO = statisticActiveCount(null, now);
        long count = orderMapper.count();
        return ActiveStatisticVO.builder()
                .dateInfo(dateStatisticVO)
                .monthInfo(monthStatisticVO)
                .yearInfo(yearStatisticVO)
                .totalInfo(totalStatisticVO)
                .deviceTotal(count)
                .build();
    }

    /**
     * 获取当天每个渠道的激活两
     */
    @Override
    public Integer statisticActiveCount(LocalDateTime start, Integer channelId) {
        return statisticActiveCount(start, LocalDateTime.now(), channelId);
    }

    private StatisticVO statisticActiveCount(LocalDateTime start) {
        return statisticActiveCount(start, LocalDateTime.now());
    }

    private StatisticVO statisticActiveCount(LocalDateTime start, LocalDateTime end) {
        BigDecimal amount = BigDecimal.ZERO;
        Integer count = 0;
        LambdaQueryWrapper<Order> orderWrapper = new LambdaQueryWrapper<Order>()
                .eq(Order::getDeviceStatus, 1);
        Optional.ofNullable(start).ifPresent(s -> orderWrapper.ge(Order::getActiveTime, s));
        Optional.ofNullable(end).ifPresent(e -> orderWrapper.le(Order::getActiveTime, e));
        List<Order> list = list(orderWrapper);
        if (CollectionUtil.isNotEmpty(list)) {
            count = list.size();
            Long rebate = list.stream().map(Order::getRebate).filter(Objects::nonNull).reduce(Long::sum).orElse(0L);
            amount = new BigDecimal(rebate).divide(BigDecimal.valueOf(100), 2);
        }
        DecimalFormat df = new DecimalFormat("0.00");
        return StatisticVO.builder().amount(df.format(amount)).count(count).build();
    }

    private StatisticVO statisticActiveCount(StatisticActiveCount statisticActiveCount) {
        BigDecimal amount = BigDecimal.ZERO;
        Integer count = 0;
        LambdaQueryWrapper<Order> orderWrapper = new LambdaQueryWrapper<Order>()
                .eq(Order::getDeviceStatus, 1);
        Optional.ofNullable(statisticActiveCount.getStart()).ifPresent(s -> orderWrapper.ge(Order::getActiveTime, s));
        Optional.ofNullable(statisticActiveCount.getEnd()).ifPresent(e -> orderWrapper.le(Order::getActiveTime, e));
        Optional.ofNullable(statisticActiveCount.getChannelId()).ifPresent(channelId -> orderWrapper.le(Order::getChannelId, channelId));
        List<Order> list = list(orderWrapper);
        if (CollectionUtil.isNotEmpty(list)) {
            count = list.size();
            Long rebate = list.stream().map(Order::getRebate).filter(Objects::nonNull).reduce(Long::sum).orElse(0L);
            amount = new BigDecimal(rebate).divide(BigDecimal.valueOf(100), 2);
        }
        DecimalFormat df = new DecimalFormat("0.00");
        return StatisticVO.builder().amount(df.format(amount)).date(statisticActiveCount.getDate()).count(count).build();
    }

    /**
     * 获取当天每个渠道的激活量
     */
    private Integer statisticActiveCount(LocalDateTime start, LocalDateTime end, Integer channelId) {
        LambdaQueryWrapper<Order> orderWrapper = new LambdaQueryWrapper<Order>()
                .eq(Order::getChannelId, channelId)
                .eq(Order::getDeviceStatus, 1);
        Optional.ofNullable(start).ifPresent(s -> orderWrapper.ge(Order::getActiveTime, s));
        Optional.ofNullable(end).ifPresent(e -> orderWrapper.le(Order::getActiveTime, e));
        List<Order> list = list(orderWrapper);
        if (CollectionUtil.isEmpty(list)) {
            return 0;
        }
        Map<Integer, List<Order>> listMap = list.stream().collect(Collectors.groupingBy(Order::getChannelId));
        return listMap.values().size();
    }

    @Override
    public Long totalRebate(Integer channelId, Integer deviceStatus, LocalDateTime localDateTime) {
        return orderMapper.totalRebate(channelId, deviceStatus, localDateTime);
    }


    @Override
    @Transactional
    public R importDevice(List<ImportOrderDTO> voList, BindingResult bindingResult) {
        log.info("device size is {}", voList.size());
        PigUser user = SecurityUtils.getUser();
        List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
        BusinessAssert.isTrue(CollectionUtil.isNotEmpty(voList), "设计集合不能为空");
        List<Order> orders = new ArrayList<>();
        List<Channel> channelList = channelService.list(new LambdaQueryWrapper<Channel>().eq(Channel::getIsDelete, 0));
        Map<String, Channel> channelMap = channelList.stream().collect(Collectors.toMap(Channel::getName, Function.identity()));
        for (int i = 0; i < voList.size(); i++) {
            ImportOrderDTO importOrderDTO = voList.get(i);
            Set<String> errorMsgS = new HashSet<>();
            Channel channel = channelMap.getOrDefault(importOrderDTO.getName().trim(), null);
            if (Objects.isNull(channel)) {
                errorMsgS.add(String.format("%s 不存在,请先在渠道管理中进行添加", importOrderDTO.getName()));
            } else {
                Order order = getOne(new LambdaQueryWrapper<Order>()
                        .eq(Order::getDeviceNo, importOrderDTO.getDeviceNo()));
                LocalDateTime now = LocalDateTime.now();
                if (Objects.isNull(order)) {
                    order = new Order();
                    order.setCreateTime(now);
                    order.setCreateBy(user.getUsername());
                }
                order.setChannelId(channel.getId());
                order.setUpdateTime(now);
                order.setUpdateBy(user.getUsername());
                order.setDeviceNo(importOrderDTO.getDeviceNo());
                if (Objects.nonNull(importOrderDTO.getCardNo())) {
                    order.setCardNo(importOrderDTO.getCardNo());
                }
                if (Objects.nonNull(importOrderDTO.getRebate())) {
                    order.setRebate(BigDecimal.valueOf(importOrderDTO.getRebate()).multiply(BigDecimal.valueOf(100)).longValue());
                }
                if (Objects.nonNull(importOrderDTO.getActivateStyle())) {
                    order.setActivateStyle(importOrderDTO.getActivateStyle());
                }
                orders.add(order);
            }
            if (CollectionUtil.isNotEmpty(errorMsgS)) {
                errorMessageList.add(new ErrorMessage((long) (i + 2), errorMsgS));
            }
        }
        if (CollUtil.isNotEmpty(errorMessageList)) {
            // todo 此处定义专门捕获菜单表格导入的抛出异常
            return R.failed(errorMessageList);
        }
        //保存订单信息
        if (CollUtil.isNotEmpty(orders)) {
            log.info("批量更新或保存订单信息");
            saveOrUpdateBatch(orders);
        }
        return R.ok();
    }


    /**
     * 标签管理中的订单导入接口
     *
     * @param voList
     * @param bindingResult
     */
    @Override
    @Transactional
    public R importOrder(List<ETCDetailDTO> voList, BindingResult bindingResult) {
        List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
        if (CollUtil.isNotEmpty(errorMessageList)) {
            return R.failed(errorMessageList);
        }
        PigUser user = SecurityUtils.getUser();
        List<Order> orders = new ArrayList<>();
        if (CollectionUtil.isEmpty(voList)) {
            Set<String> errorMsgS = new HashSet<>();
            errorMsgS.add("激活明细不能为空");
            errorMessageList.add(new ErrorMessage(errorMsgS));
            return R.failed(errorMessageList);
        }
        Object o = redisTemplate.boundValueOps(STATISTIC_INFO).get();
        if (Objects.nonNull(o)) {
            Set<String> errorMsgS = new HashSet<>();
            errorMsgS.add("正在计算中请稍候再试");
            errorMessageList.add(new ErrorMessage(errorMsgS));
            return R.failed(errorMessageList);
        }

        List<Channel> channelList = channelService.list(new LambdaQueryWrapper<Channel>().eq(Channel::getIsDelete, 0));
        Map<String, Channel> channelMap = channelList.stream().collect(Collectors.toMap(Channel::getName, Function.identity()));
        for (int i = 0; i < voList.size(); i++) {
            ETCDetailDTO detailDTO = voList.get(i);
            Set<String> errorMsgS = new HashSet<>();
            Channel channel = channelMap.getOrDefault(detailDTO.getName().trim(), null);
            if (Objects.isNull(channel)) {
                errorMsgS.add(String.format("%s 不存在,请先在渠道管理中进行添加", detailDTO.getName()));
            } else {
                Order order = getOne(new LambdaQueryWrapper<Order>().eq(Order::getDeviceNo, detailDTO.getDeviceNo()));
                LocalDateTime now = LocalDateTime.now();
                if (Objects.isNull(order)) {
                    order = new Order();
                    order.setCreateTime(now);
                    order.setCreateBy(user.getUsername());
                }
                order.setChannelId(channel.getId());
                order.setCustomer(detailDTO.getCustomer());
                order.setDeviceNo(detailDTO.getDeviceNo());
                if (Objects.nonNull(detailDTO.getActivateTime()) || Objects.isNull(order.getActiveTime())) {
                    order.setActiveTime(detailDTO.getActivateTime());
                    order.setDeviceStatus(1);
                }
                if (Objects.nonNull(detailDTO.getRebate())) {
                    order.setRebate(BigDecimal.valueOf(detailDTO.getRebate()).multiply(BigDecimal.valueOf(100)).longValue());
                }
                if (Objects.nonNull(detailDTO.getActivateStyle())) {
                    order.setActivateStyle(detailDTO.getActivateStyle());
                }
                order.setVehicleColor(detailDTO.getVehicleColor());
                order.setOrderNo(detailDTO.getOrderNo());
                order.setVehicleKind(detailDTO.getVehicleKind());
                order.setVehicleNo(detailDTO.getVehicleNo());
                order.setSignTime(detailDTO.getSignTime());
                order.setVehicleType(detailDTO.getVehicleType());
                order.setCardNo(detailDTO.getCardNo());
                order.setUpdateTime(now);
                order.setUpdateBy(user.getUsername());
                orders.add(order);
            }
            if (CollectionUtil.isNotEmpty(errorMsgS)) {
                errorMessageList.add(new ErrorMessage((long) (i + 2), errorMsgS));
            }
        }

        //保存订单信息
        saveOrUpdateBatch(orders);

        applicationEventPublisher.publishEvent(new StatisticsEvent(STATISTIC_INFO));
        return R.ok();
    }

    /**
     * @param dto 入参信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrder(OrderUpdateDTO dto) {
        //Order order = OrderConverter.INSTANCE.toOrder(dto);
        Order order = JSONUtil.toBean(JSONUtil.toJsonStr(dto), Order.class);
        updateById(order);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatchOrder(OrderDeleteDTO dto) {
        removeByIds(dto.getOrderIds());
    }

    /**
     * @param dto 入参信息
     * @return
     */
    @Override
    public BasePageVO<OrderVO> queryOrderList(OrderListDTO dto) {
        List<Channel> channels = channelService.list();
        Map<Integer, Channel> channelMap = channels.stream().collect(Collectors.toMap(Channel::getId, Function.identity()));
        LambdaQueryWrapper<Order> orderWrapper = new LambdaQueryWrapper<>();
        Optional.ofNullable(dto.getChannelId()).ifPresent(channelId -> orderWrapper.eq(Order::getChannelId, channelId));
        Optional.ofNullable(dto.getDeviceNo()).ifPresent(deviceNo -> orderWrapper.eq(Order::getDeviceNo, deviceNo));
        Optional.ofNullable(dto.getOrderNo()).ifPresent(orderNo -> orderWrapper.eq(Order::getOrderNo, orderNo));
        Optional.ofNullable(dto.getDeviceStatus()).ifPresent(status -> orderWrapper.eq(Order::getDeviceStatus, status));
        String channelName = dto.getChannelName();
        if (StrUtil.isNotBlank(channelName)) {
            List<Integer> ids = new ArrayList<>();
            ids.add(0);
            for (Channel value : channelMap.values()) {
                String name = value.getName();
                if (name.equalsIgnoreCase(channelName) || name.contains(dto.getChannelName())) {
                    ids.add(value.getId());
                }
            }
            ids.add(0);
            orderWrapper.in(Order::getChannelId, ids);
        }
        Optional.ofNullable(dto.getMenuType()).ifPresent(
                menuType -> {
                    if (menuType.equals(1)) {
                        orderWrapper.and(wp -> wp.isNull(Order::getOrderNo).or().eq(Order::getOrderNo, ""));
                    } else {
                        orderWrapper.isNotNull(Order::getOrderNo).ne(Order::getOrderNo, "");
                    }
                }
        );
        if (CollectionUtil.isNotEmpty(channelMap.keySet())) {
            orderWrapper.in(Order::getChannelId, channelMap.keySet().toArray());
        }
        Page<Order> page = page(new Page<>(dto.getPageIndex(), dto.getPageSize()), orderWrapper);
        List<Order> orders = page.getRecords();
        List<OrderVO> orderVOS = orders.stream().map(order -> {
            Channel channel = channelMap.get(order.getChannelId());
            if (Objects.isNull(channel)) {
                log.info("order channel is empty,{}", JSONUtil.toJsonStr(order));
            }
            Channel parentChannel = null;
            if (Objects.nonNull(channel.getParentId())) {
                parentChannel = channelMap.get(channel.getParentId());
            }
            OrderVO orderVO = JSONUtil.toBean(JSONUtil.toJsonStr(order), OrderVO.class);
            Long rebate = order.getRebate();
            BigDecimal rebateAmount = BigDecimal.ZERO;
            if (null != rebate) {
                rebateAmount = BigDecimal.valueOf(order.getRebate()).divide(BigDecimal.valueOf(100), 2);
            }
            DecimalFormat df = new DecimalFormat("0.00");
            orderVO.setRebateAmount(df.format(rebateAmount));
            orderVO.setChannelName(channel.getName());
            orderVO.setChannelLevel(LevelEnum.getLevelName(channel.getLevel()));
            orderVO.setParentChannelName(Objects.isNull(parentChannel) ? "-" : parentChannel.getName());
            return orderVO;

        }).collect(Collectors.toList());
        return BasePageVO.instance(orderVOS, page.getTotal());
    }
}
